<!doctype html>
<html>
    <head>
        <title>HT for Web 交互式图元手册</title>
        <meta charset="UTF-8">    
        <meta name="viewport" content="user-scalable=yes, width=1024">
        <style type="text/css"> 
            h1, h2, h3, h4, h5, h6, p, blockquote {
                margin: 0;
                padding: 0;
            }
            body {
                font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", Arial, sans-serif;
                font-size: 13px;
                line-height: 18px;
                color: #737373;
                background-color: white; 
                margin: 10px 13px 10px 13px;
            }
            table {
                margin: 10px 0 15px 0;
                border-collapse: collapse;
            }
            td,th {	
                border: 1px solid #ddd;
                padding: 3px 10px;
            }
            th {
                padding: 5px 10px;	
            }
            a {
                color: #0069d6;
            }
            a:hover {
                color: #0050a3;
                text-decoration: none;
            }
            a img {
                border: none;
            }
            p {
                margin-bottom: 9px;
            }
            h1, h2, h3, h4, h5, h6 {
                color: #404040;
                line-height: 36px;
            }
            h1 {
                margin-bottom: 18px;
                font-size: 30px;
            }
            h2 {
                font-size: 24px;
            }
            h3 {
                font-size: 18px;
            }
            h4 {
                font-size: 16px;
            }
            h5 {
                font-size: 14px;
            }
            h6 {
                font-size: 13px;
            }
            hr {
                margin: 0 0 19px;
                border: 0;
                border-bottom: 1px solid #ccc;
            }
            blockquote {
                padding: 13px 13px 21px 15px;
                margin-bottom: 18px;
                font-family:georgia,serif;
                font-style: italic;
            }
            blockquote:before {
                content:"\201C";
                font-size:40px;
                margin-left:-10px;
                font-family:georgia,serif;
                color:#eee;
            }
            blockquote p {
                font-size: 14px;
                font-weight: 300;
                line-height: 18px;
                margin-bottom: 0;
                font-style: italic;
            }
            code, pre {
                font-family: Monaco, Andale Mono, Courier New, monospace;
            }
            code {
                background-color: #fee9cc;
                color: rgba(0, 0, 0, 0.75);
                padding: 1px 3px;
                font-size: 12px;
                -webkit-border-radius: 3px;
                -moz-border-radius: 3px;
                border-radius: 3px;
            }
            pre {
                display: block;
                padding: 14px;
                margin: 0 0 18px;
                line-height: 16px;
                font-size: 11px;
                border: 1px solid #d9d9d9;
                white-space: pre-wrap;
                word-wrap: break-word;
            }
            pre code {
                background-color: #fff;
                color:#737373;
                font-size: 11px;
                padding: 0;
            }
            sup {
                font-size: 0.83em;
                vertical-align: super;
                line-height: 0;
            }
            * {
                -webkit-print-color-adjust: exact;
            }
            @media screen and (min-width: 914px) {
                body {
                    width: 854px;
                    margin:10px auto;
                }
            }
            @media print {
                body,code,pre code,h1,h2,h3,h4,h5,h6 {
                    color: black;
                }
                table, pre {
                    page-break-inside: avoid;
                }
            }                        
            iframe{
                width: 100%;
                border: 1px solid #34495E;
                margin: 0;                
            }            
            .logo{
                vertical-align: middle;
            }

        </style>        
        <script>
            function init() {
                var logoSrc = '',
                    logos = document.querySelectorAll('.logo'),
                    i = 0;
                for (; i < logos.length; i++) {
                    logos[i].src = logoSrc;
                }
                
                var iframes = document.querySelectorAll('iframe'),
                    func = function(){};
                for (i=0; i < iframes.length; i++) {
                    var iframe = iframes[i];
                    
                    // a small hack to make it work on android
                    iframe.ontouchstart = func;
                    
                    var div = document.createElement('div');
                    div.innerHTML = '<a href="' + iframe.src + '" target="_blank">独立运行→</a>';
                    iframe.parentNode.insertBefore(div, iframe);
                }   
            }
        </script>    
    </head>
    <body onload="init();">
        <a href="http://www.hightopo.com"><img class="logo"></a>HT for Web 交互式图元手册
        <hr style="margin: 1px 0 20px 0">
<p>索引</p>

<ul><li><a href="#ref_overview">概述</a><ul><li><a href="#ref_purpose">用途</a></li><li><a href="#ref_feature">特点</a></li></ul></li><li><a href="#ref_node">图元</a><ul><li><a href="#ref_liveNode">交互式图元</a></li><li><a href="#ref_button">按钮</a></li><li><a href="#ref_toggleButton">切换按钮</a><ul><li><a href="#ref_checkbox">复选框</a></li><li><a href="#ref_radioButton">单选按钮</a></li><li><a href="#ref_switch">开关</a></li><li><a href="#ref_buttonGroup">按钮组</a></li></ul></li><li><a href="#ref_progressBar">进度条</a></li><li><a href="#ref_slider">滑动条</a></li><li><a href="#ref_spinner">微调按钮</a></li><li><a href="#ref_combobox">组合框</a></li></ul></li></ul>

<hr/>

<div id="ref_overview"></div>

<h2>概述</h2>

<p>交互式图元是普通图元(<code>ht.Node</code>)的扩展，包含按钮(<code>ht.ButtonNode</code>)、切换按钮(<code>ht.ToggleButtonNode</code>)、复选框(<code>ht.CheckboxNode</code>)、
单选按钮(<code>ht.RadioButtonNode</code>)、开关(<code>ht.SwitchNode</code>)、进度条(<code>ht.ProgressBarNode</code>)、滑动条(<code>ht.SliderNode</code>)、
微调按钮(<code>ht.SpinnerNode</code>)、组合框(<code>ht.ComboboxNode</code>)等。</p>

<p><iframe src="examples/example_overview.html" style="height:150px"></iframe></p>

<pre><code>var liveNodes = [
    &#39;Button&#39;,
    &#39;ToggleButton&#39;,
    &#39;Checkbox&#39;,
    &#39;Switch&#39;,
    &#39;RadioButton&#39;,
    &#39;ProgressBar&#39;,
    &#39;Slider&#39;,
    &#39;Spinner&#39;,
    &#39;Combobox&#39;,
    &#39;RadioButton&#39;
];

function init () {
    liveNodes.forEach(function (type, i) {
        var node = addNode(type, 120 + (i % 5) * 150, 50 + Math.floor(i / 5) * 50);
        if (i % 5 === 4) buttonGroup.add(node);
    });

    initBody();
}

function addNode (type, x, y) {
    // Create live node
    var node = new ht[type + &#39;Node&#39;]();
    node.setPosition(x, y);
    dataModel.add(node);

    // Set button label
    node.s(&#39;live.label&#39;, type);

    // Set toggle button selected
    if (node.setSelected) node.setSelected(true);

    // Set value for ProgressBar, Slider and Spinner
    if (node.setValue) node.setValue(50);

    // Setup Combobox
    if (node.setItems) {
        node.setItems(liveNodes);
        node.setSelectedItem(type);
    }
}</code></pre>

<div id="ref_purpose"></div>

<h3>用途</h3>

<p>交互式图元不同于一般的图元，一般的图元仅用于图形化展示信息，但交互式图元可响应用户鼠标、键盘和触摸屏输入，和普通的用户界面组件功能相当，
可用在监控领域中呈现图形用户界面，给被监控对象发送指令，更改被监控对象的指标。比如用开关(<code>ht.SwitchNode</code>)控制照明灯的打开与关闭，
用滑动条(<code>ht.SliderNode</code>)控制照明灯的明暗等等。</p>

<div id="ref_feature"></div>

<h3>特点</h3>

<ul><li>在图形组件(<code>ht.graph.GraphView</code>)上呈现各种交互式图元</li><li>充分利用<code>HT</code>的矢量特性，所有交互式图元的外观由矢量图形构成</li><li>可以定制所有交互式图元的外观</li></ul>

<div id="ref_node"></div>

<h2>图元</h2>

<div id="ref_liveNode"></div>

<h3>交互式图元</h3>

<p>交互式图元(<code>ht.LiveNode</code>)是所有交互式图元的基类，默认宽<code>width</code>为100像素，高<code>height</code>为35像素，包含如下状态，可以根据这些状态定制交互式图元外观：</p>

<ul><li><code>enabled</code> 是否启用(<code>boolean</code>)，未启用的图元显示为灰色，通过<code>isEnabled()</code>获取值、<code>setEnabled(value)</code>设置值</li><li><code>editable</code> 是否可编辑(<code>boolean</code>)，不可编辑的图元不能响应鼠标、键盘等事件，通过<code>isEditable()</code>获取值、<code>setEditable(value)</code>设置值</li><li><code>hover</code> 鼠标是否悬停(<code>boolean</code>)，通过<code>isHover()</code>获取值、<code>setHover(value)</code>设置值</li><li><code>pressed</code> 是否被按下(<code>boolean</code>)，通过<code>isPressed()</code>获取值、<code>setPressed(value)</code>设置值</li></ul>

<p>可以通过如下<code>Style</code>属性设置交互式图元的样式：</p>

<ul><li><code>live.shape</code> 按钮形状，默认值为<code>&#39;rect&#39;</code></li><li><code>live.border.width</code> 按钮边框宽度，默认值为<code>1</code></li><li><code>live.border.color</code> 按钮边框颜色</li><li><code>live.gradient</code> 按钮渐变色类型，默认值为<code>&#39;&#39;</code></li><li><code>live.gradient.color</code> 按钮渐变色</li><li><code>live.background</code> 按钮背景色</li><li><code>live.background.disabled</code> 按钮被禁用时背景色，默认值为<code>rect</code></li><li><code>live.background.hover</code> 鼠标悬留时背景色</li><li><code>live.background.active</code> 按钮被按下时背景色</li><li><code>live.label</code> 按钮文字</li><li><code>live.label.offset.x</code> 按钮文字水平偏移量，默认值为<code>0</code></li><li><code>live.label.offset.y</code> 按钮文字垂直偏移量，默认值为<code>0</code></li><li><code>live.label.align</code> 按钮文字对其方式，默认值为<code>center</code></li><li><code>live.label.font</code> 按钮文字字体，默认值为<code>undefined</code></li><li><code>live.label.color</code> 按钮文字颜色</li><li><code>live.label.disabled</code> 按钮被禁用时文字颜色</li><li><code>live.label.hover</code> 鼠标悬留时文字颜色</li><li><code>live.label.active</code> 按钮被按下时文字颜色</li></ul>

<p>交互式图元的背景颜色为<code>LiveNode#getBackground()</code>，文字颜色为<code>LiveNode#getForeground()</code>，这些颜色会根据交互式图元的
<code>hover</code>，<code>pressed</code>和<code>enabled</code>等状态改变。</p>

<h5><code>LiveNode#getBackground()</code></h5>

<ul><li>return <code>string</code> 背景颜色</li></ul>

<h5><code>LiveNode#getForeground()</code></h5>

<ul><li>return <code>string</code> 文字颜色</li></ul>

<div id="ref_button"></div>

<h3>按钮</h3>

<p>按钮(<code>ht.ButtonNode</code>)可以响应用户的鼠标点击和键盘<code>Space</code>、<code>Enter</code>事件，执行<code>onClicked</code>方法指定的动作。
按钮需要先获得焦点（通过鼠标点击，或者<code>Tab</code>键切换焦点）才能响应键盘事件。按钮的图片名称为<code>button_image</code>，矢量描述如下：</p>

<pre><code>ht.Default.setImage(&#39;button_image&#39;, {
    width: { func: &#39;getWidth&#39; },
    height: { func: &#39;getHeight&#39; },
    comps: [
        {
            type: { func: &#39;style@live.shape&#39; },
            borderWidth: { func: &#39;style@live.border.width&#39; },
            borderColor: { func: &#39;style@live.border.color&#39; },
            gradient: { func: &#39;style@live.gradient&#39; },
            gradientColor: { func: &#39;style@live.gradient.color&#39; },
            background: { func: &#39;getBackground&#39; },
            rect: [0, 0, 1, 1],
            relative: true
        },
        {
            type: &#39;text&#39;,
            text: { func: &#39;style@live.label&#39; },
            align: { func: &#39;style@live.label.align&#39; },
            color: { func: &#39;getForeground&#39; },
            font: { func: &#39;style@live.label.font&#39; },
            rect: [0, 0, 1, 1],
            relative: true,
            offsetX: { func: &#39;style@live.label.offset.x&#39; },
            offsetY: { func: &#39;style@live.label.offset.y&#39; }
        }
    ]
});</code></pre>

<p>下面的例子演示了普通按钮、圆角按钮、不可编辑按钮和不可用按钮:</p>

<p><iframe src="examples/example_button.html" style="height:100px"></iframe></p>

<pre><code>addButton(200, 50, &#39;Normal&#39;);

// Set button shape to &#39;roundRect&#39;
addButton(350, 50, &#39;Round rect&#39;).s(&#39;live.shape&#39;, &#39;roundRect&#39;);

addButton(500, 50, &#39;Uneditable&#39;, false);
addButton(650, 50, &#39;Disabled&#39;, true, false);

function addButton(x, y, label, editable, enable) {
    var button = new ht.ButtonNode();
    button.setPosition(x, y);

    // Set button label
    button.s(&#39;live.label&#39;, label);

    // Set button editable
    if (editable !== undefined) button.setEditable(editable);

    // Set button enabled
    if (enable !== undefined) button.setEnabled(enable);

    // Add click action
    var clicked = false;
    button.onClicked = function () {
        if (clicked) return;
        clicked = true;
        alert(button.s(&#39;live.label&#39;) + &#39; is clicked&#39;);
        setTimeout(function () {
            clicked = false;
        }, 100);
    };

    dataModel.add(button);
    return button;
}</code></pre>

<p>下面的例子演示了如何用按钮模拟iOS7的拨号键盘：</p>

<p><iframe src="examples/example_buttonDial.html" style="height:350px"></iframe></p>

<p>其中数字按钮图片描述如下：</p>

<pre><code>var image = clone(ht.Default.getImage(&#39;button_image&#39;));
image.comps.push({
    type: &#39;text&#39;,
    text: { func: &#39;attr@label2&#39; },
    rect: [0, 0, 1, 1],
    relative: true,
    offsetY: 8,
    color: {
        func: function (data) {
            return data.isPressed() ? &#39;white&#39; : &#39;black&#39;;
        }
    },
    font: &#39;12px arial, sans-serif&#39;,
    align: &#39;center&#39;
});
ht.Default.setImage(&#39;DialButton&#39;, image);</code></pre>

<p>先复制了默认实现，再添加了第二个标签<code>label2</code></p>

<pre><code>function clone (o) {
    var result;
    if(o instanceof Array){
        result = [];
        o.forEach(function (e) {
            result.push(clone(e, true));
        });
        return result;
    }
    if(typeof o === &#39;object&#39;){
        var name;
        result = {};
        for (name in o) {
            result[name] = clone(o[name]);
        }
        return result;
    }
    return o;
}</code></pre>

<p>删除按钮图片描述如下：</p>

<pre><code>ht.Default.setImage(&#39;ClearButton&#39;, {
    width: { func: &#39;getWidth&#39; },
    height: { func: &#39;getHeight&#39; },
    comps: [
        {
            type: &#39;circle&#39;,
            background: &#39;#0B65F6&#39;,
            rect: [0.15, 0.15, 0.7, 0.7],
            relative: true
        },
        {
            type: &#39;shape&#39;,
            background: &#39;#0B65F6&#39;,
            points: {
                func: function (data) {
                    var width = data.getWidth(),
                        height = data.getHeight();
                    return [
                        width * 0.03, height * 0.5, width * 0.3, height * 0.25, width * 0.3, height * 0.75
                    ];
                }
            }
        },
        {
            type: &#39;shape&#39;,
            borderColor: &#39;#FFF&#39;,
            borderWidth: {
                func: function (data) {
                    return data.getWidth() * 0.1;
                }
            },
            points: {
                func: function (data) {
                    var width = data.getWidth(),
                        height = data.getHeight();
                    return [
                        width * 0.35, height * 0.35, width * 0.65, height * 0.65,
                        width * 0.65, height * 0.35, width * 0.35, height * 0.65
                    ];
                }
            },
            segments: [1, 2, 1, 2]
        }
    ]
});</code></pre>

<h5><code>ButtonNode#onClicked</code></h5>

<ul><li><code>function (e)</code> 按钮被点击后执行的回调函数<ul><li><code>e</code> 鼠标事件</li></ul></li></ul>

<p>设置按钮被点击后执行的动作</p>

<pre><code>button.onClicked = function (e) {
    inputNode.setName(inputNode.getName() + this.s(&#39;live.label&#39;));
};</code></pre>

<div id="ref_toggleButton"></div>

<h3>切换按钮</h3>

<p>切换按钮(<code>ht.ToggleButtonNode</code>)继承于按钮(<code>ht.ButtonNode</code>)，增加了<code>isSelected</code>和<code>setSelected</code>方法，用于获取和设置是否被按下或选中的状态，
并在选中状态变化时回调<code>onChanged</code>函数。</p>

<h5><code>ToggleButtonNode#isSelected()</code></h5>

<ul><li>return <code>boolean</code> 获取切换按钮的选中状态</li></ul>

<h5><code>ToggleButtonNode#setSelected(value)</code></h5>

<ul><li>value <code>boolean</code> 设置切换按钮的选中状态</li></ul>

<h5><code>ToggleButtonNode#onChanged</code></h5>

<ul><li><code>function (value)</code> 切换按钮选中状态变化时的回调函数<ul><li><code>value</code> 新的选中状态</li></ul></li></ul>

<div id="ref_exampleToggleButton"></div>

<p>下面的例子演示了如何定制切换按钮：</p>

<p><iframe src="examples/example_toggleButton.html" style="height:150px"></iframe></p>

<p>定制切换按钮的图片如下：</p>

<pre><code>function setRecentsImage () {
    ht.Default.setImage(&#39;Recents&#39;, {
        width: { func: &#39;getWidth&#39; },
        height: { func: &#39;getHeight&#39; },
        comps: [
            {
                type: &#39;circle&#39;,
                borderColor: {
                    func: function (data) {
                        return data.isSelected() ? &#39;#0B6AFF&#39; : &#39;#929292&#39;;
                    }
                },
                borderWidth: 3,
                rect: [0.1, 0.1, 0.8, 0.8],
                relative: true
            },
            {
                type: &#39;shape&#39;,
                borderColor: {
                    func: function (data) {
                        return data.isSelected() ? &#39;#0B6AFF&#39; : &#39;#929292&#39;;
                    }
                },
                borderWidth: 2,
                points: {
                    func: function (data) {
                        var width = data.getWidth(),
                            height = data.getHeight();
                        return [0.5 * width, 0.15 * height, 0.5 * width, 0.5 * height, 0.2 * width, 0.5 * height];
                    }
                },
                segments: [1, 2, 2]
            }
        ]
    });
}

function setKeypadImage () {
    var comps = [{
        type: &#39;rect&#39;,
        rect: [0, 0, 1, 1],
        relative: true,
        background: &#39;white&#39;
    }];
    var getRect = function (i, j) {
        var width = 1 / 4,
            height = 1 / 4,
            radius = Math.min(width, height)/2,
            xgap = width/4,
            ygap = height/4;
        return [width * i - radius + xgap * (i - 2), height * j - radius + ygap * (j - 2), radius * 2, radius * 2];
    };
    for (var i=1; i&lt;4; i++) {
        for (var j=1; j&lt;4; j++) {
            comps.push({
                type: &#39;circle&#39;,
                rect: getRect(i, j),
                relative: true,
                background: {
                    func: function (data) {
                        return data.isSelected() ? &#39;#0B6AFF&#39; : &#39;#929292&#39;;
                    }
                }
            });
        }
    }
    ht.Default.setImage(&#39;Keypad&#39;, {
        width: { func: &#39;getWidth&#39; },
        height: { func: &#39;getHeight&#39; },
        comps: comps
    });
}</code></pre>

<p>切换按钮选中状态变化时，更改标签颜色：</p>

<pre><code>toggleButton.onChanged = function (value) {
    this.s(&#39;label.color&#39;, value ? &#39;#0B6AFF&#39; : &#39;#929292&#39;);
};</code></pre>

<div id="ref_checkbox"></div>

<h4>复选框</h4>

<p>复选框(<code>ht.CheckboxNode</code>)继承于切换按钮(<code>ht.ToggleButton</code>)，功能和切换按钮一样，仅仅外观不一样，图片名称为<code>checkbox_image</code>，矢量描述如下：</p>

<pre><code>ht.Default.setImage(&#39;checkbox_image&#39;, {
    width: { func: &#39;getWidth&#39; },
    height: { func: &#39;getHeight&#39; },
    comps: [
        {
            type: { func: &#39;style@live.shape&#39; },
            background: { func: &#39;getBackground&#39; },
            rect: {
                func: function (data) {
                    var width = data._width, height = data._height;
                    return {x: height * 0.1, y: height * 0.2, width: height * 0.6, height: height * 0.6};
                }
            }
        },
        {
            type: &#39;shape&#39;,
            points: {
                func: function (data) {
                    var width = data._width, height = data._height;
                    return [0.3 * height, 0.5 * height, 0.4 * height, 0.6 * height, 0.55 * height, 0.35 * height];
                }
            },
            borderWidth: {
                func: function (data) {
                    return data._height * 0.05;
                }
            },
            borderColor: &#39;#FFFFFF&#39;,
            visible: {
                func: function (data) {
                    return data._selected || data._hover;
                }
            }
        },
        {
            type: &#39;text&#39;,
            text: { func: &#39;style@live.label&#39; },
            align: { func: &#39;style@live.label.align&#39; },
            color: { func: &#39;getForeground&#39; },
            font: { func: &#39;style@live.label.font&#39; },
            rect: {
                func: function (data) {
                    var width = data._width, height = data._height;
                    return {x: height * 0.8, y: 0, width: width - height * 0.8, height: height};
                }
            },
            offsetX: { func: &#39;style@live.label.offset.x&#39; },
            offsetY: { func: &#39;style@live.label.offset.y&#39; }
        }
    ]
});</code></pre>

<div id="ref_radioButton"></div>

<h4>单选按钮</h4>

<p>单选按钮(<code>ht.RadioButtonNode</code>)继承于切换按钮(<code>ht.ToggleButton</code>)，功能和切换按钮一样，仅仅外观不一样，图片名称为<code>radioButton_image</code>，矢量描述如下：</p>

<pre><code>ht.Default.setImage(&#39;radioButton_image&#39;, {
    width: { func: &#39;getWidth&#39; },
    height: { func: &#39;getHeight&#39; },
    comps: [
        {
            type: &#39;circle&#39;,
            borderWidth: {
                func: function (data) {
                    return data._height * 0.1;
                }
            },
            borderColor: { func: &#39;getBackground&#39; },
            rect: {
                func: function (data) {
                    var width = data._width, height = data._height;
                    return {x: height * 0.1, y: height * 0.2, width: height * 0.6, height: height * 0.6};
                }
            }
        },
        {
            type: &#39;circle&#39;,
            background: { func: &#39;getBackground&#39; },
            rect: {
                func: function (data) {
                    var width = data._width, height = data._height;
                    return {x: height * 0.3, y: height * 0.4, width: height * 0.2, height: height * 0.2};
                }
            },
            visible: {
                func: function (data) {
                    return data._selected || data._hover;
                }
            }
        },
        {
            type: &#39;text&#39;,
            text: { func: &#39;style@live.label&#39; },
            align: { func: &#39;style@live.label.align&#39; },
            color: { func: &#39;getBackground&#39; },
            font: { func: &#39;style@live.label.font&#39; },
            rect: {
                func: function (data) {
                    var width = data._width, height = data._height;
                    return {x: height * 0.8, y: 0, width: width - height * 0.8, height: height};
                }
            },
            offsetX: { func: &#39;style@live.label.offset.x&#39; },
            offsetY: { func: &#39;style@live.label.offset.y&#39; }
        }
    ]
});</code></pre>

<div id="ref_switch"></div>

<h4>开关</h4>

<p>开关(<code>ht.SwitchNode</code>)继承于切换按钮(<code>ht.ToggleButton</code>)，功能和切换按钮一样，仅仅外观不一样，图片名称为<code>switch_image</code>，矢量描述如下：</p>

<pre><code>ht.Default.setImage(&#39;switch_image&#39;, {
    width: { func: &#39;getWidth&#39; },
    height: { func: &#39;getHeight&#39; },
    comps: [
        {
            type: { func: &#39;style@live.shape&#39; },
            borderWidth: { func: &#39;style@live.border.width&#39; },
            borderColor: { func: &#39;style@live.border.color&#39; },
            background: { func: &#39;style@switch.background&#39; },
            opacity: {
                func: function (data) {
                    return data._enabled ? 1 : 0.5;
                }
            },
            rect: [0, 0, 1, 1],
            relative: true
        },
        {
            type: &#39;circle&#39;,
            background: { func: &#39;getBackground&#39; },
            opacity: {
                func: function (data) {
                    return data._enabled ? 1 : 0.5;
                }
            },
            rect: {
                func: function (data) {
                    var size = 30, gap = 10, selected = data._selected;
                    return {x: selected ? data._width - gap - size : gap, y: (data._height - size) / 2, width: size, height: size};
                }
            }
        },
        {
            type: &#39;text&#39;,
            text: {
                func: function (data) {
                    return data.s(data._selected ? &#39;switch.text.on&#39; : &#39;switch.text.off&#39;);
                }
            },
            rect: [17, 1, 1],
            relative: true,
            offsetX: {
                func: function (data) {
                    return data._selected ? -10 : 10;
                }
            },
            color: { func: &#39;getForeground&#39; },
            font: { func: &#39;style@live.label.font&#39; },
            align: { func: &#39;style@live.label.align&#39; }
        }
    ]
});</code></pre>

<p>可以通过如下<code>Style</code>属性设置开关的样式：</p>

<ul><li><code>switch.background</code> 开关关闭时背景</li><li><code>switch.text.on</code> 开关打开时文字，默认值为<code>&#39;ON&#39;</code></li><li><code>switch.text.off</code> 开关关闭时文字，默认值为<code>&#39;OFF&#39;</code></li></ul>

<p>下面是定制开关的例子：</p>

<p><iframe src="examples/example_switch.html" style="height:100px"></iframe></p>

<div id="ref_buttonGroup"></div>

<h4>按钮组</h4>

<p>按钮组(<code>ht.ButtonGroup</code>)可以包含切换按钮、复选框、单选按钮和开关等交互式图元，当一个图元被选中时，其他图元自动取消选中，参考<a href="#ref_exampleToggleButton">定制切换按钮例子</a></p>

<h5><code>ButtonGroup(items)</code></h5>

<ul><li><code>items</code> <code>array</code> 包含切换按钮的集合，默认值为undefined</li></ul>

<p>按钮组构造函数</p>

<h5><code>ButtonGroup#add(item)</code></h5>

<ul><li><code>item</code> <code>ht.ToggleButton</code> 切换按钮</li></ul>

<p>添加切换按钮</p>

<h5><code>ButtonGroup#addAll(items)</code></h5>

<ul><li><code>items</code> <code>arry</code> 包含切换按钮的集合</li></ul>

<p>添加切换按钮集合</p>

<h5><code>ButtonGroup#remove(item)</code></h5>

<ul><li><code>item</code> <code>ht.ToggleButton</code> 切换按钮</li></ul>

<p>删除切换按钮</p>

<h5><code>ButtonGroup#clear()</code></h5>

<p>删除所有切换按钮</p>

<h5><code>ButtonGroup#getItems()</code></h5>

<ul><li>return <code>array</code> 返回切换按钮集合</li></ul>

<h5><code>ButtonGroup#getSelected()</code></h5>

<ul><li>return <code>ht.ToggleButton</code> 返回被选中切换按钮</li></ul>

<h5><code>ButtonGroup#onChanged</code></h5>

<ul><li><code>function (item)</code> 按钮组被选中项变化时的回调函数<ul><li><code>item</code> <code>ht.ToggleButton</code> 新的被选中切换按钮</li></ul></li></ul>

<div id="ref_progressBar"></div>

<h3>进度条</h3>

<p>进度条(<code>ht.ProgressBarNode</code>)用于显示进度百分比，图片名称为<code>progressBar_image</code>，矢量描述如下：</p>

<pre><code>ht.Default.setImage(&#39;progressBar_image&#39;, {
    width: { func: &#39;getWidth&#39; },
    height: { func: &#39;getHeight&#39; },
    comps: [
        // Background
        {
            type: &#39;rect&#39;,
            background: { func: &#39;style@live.background&#39; },
            rect: [0, 0, 1, 1],
            relative: true
        },
        // Top
        {
            type: &#39;rect&#39;,
            background: { func: &#39;style@live.background.active&#39; },
            rect: {
                func: function (data) {
                    return [0, 0, data._value / 100, 0.5];
                }
            },
            relative: true
        },
        // Bottom
        {
            type: &#39;rect&#39;,
            background: {
                func: function (data) {
                    return ht.Default.darker(data.s(&#39;live.background.active&#39;));
                }
            },
            rect: {
                func: function (data) {
                    return [0, 0.5, data._value / 100, 0.5];
                }
            },
            relative: true
        }
    ]
});</code></pre>

<h5><code>ProgressBarNode#getValue()</code></h5>

<ul><li>return <code>number</code> 返回进度条的值，取值范围为<code>0-100</code></li></ul>

<h5><code>ProgressBarNode#setValue(value)</code></h5>

<ul><li><code>value</code> <code>number</code> 值</li></ul>

<p>设置进度条的值，取值范围为<code>0-100</code></p>

<div id="ref_slider"></div>

<h3>滑动条</h3>

<p>滑动条(<code>ht.SliderNode</code>)可以设置最大值<code>max</code>、最小值<code>min</code>和当前值<code>value</code>，通过<code>orientation</code>设置显示方向（可选值为<code>horizontal</code>或<code>h</code>、<code>vertical</code>或<code>v</code>），
通过滑动滑块更改当前值。</p>

<h5><code>SliderNode#getValue()</code></h5>

<ul><li>return <code>number</code> 返回当前值</li></ul>

<h5><code>SliderNode#setValue(value)</code></h5>

<ul><li><code>value</code> <code>number</code> 当前值</li></ul>

<p>设置滑动条当前值</p>

<h5><code>SliderNode#getMin()</code></h5>

<ul><li>return <code>number</code> 返回最小值，默认值为<code>0</code></li></ul>

<h5><code>SliderNode#setMin(value)</code></h5>

<ul><li><code>value</code> <code>number</code> 最小值</li></ul>

<p>设置滑动条最小值</p>

<h5><code>SliderNode#getMax()</code></h5>

<ul><li>return <code>number</code> 返回最大值，默认值为<code>100</code></li></ul>

<h5><code>SliderNode#setMax(value)</code></h5>

<ul><li><code>value</code> <code>number</code> 最大值</li></ul>

<p>设置滑动条最大值</p>

<h5><code>SliderNode#getOrientation()</code></h5>

<ul><li>return <code>number</code> 返回滑动条显示方向</li></ul>

<h5><code>SliderNode#setOrientation(value)</code></h5>

<ul><li><code>value</code> <code>number</code> 显示方向</li></ul>

<p>设置滑动条显示方向</p>

<h5><code>SliderNode#isHorizontal()</code></h5>

<ul><li>return <code>boolean</code> 返回滑动条是否水平显示</li></ul>

<h5><code>SliderNode#getStep()</code></h5>

<ul><li>return <code>number</code> 返回步进，默认值为<code>1</code></li></ul>

<h5><code>SliderNode#setStep(value)</code></h5>

<ul><li><code>value</code> <code>number</code> 步进</li></ul>

<p>设置滑动条步进</p>

<h5><code>SliderNode#onChanged</code></h5>

<ul><li><code>function (value)</code> 滑动条值变化时的回调函数<ul><li><code>value</code> 新的值</li></ul></li></ul>

<p>可以通过如下<code>Style</code>属性设置滑动条的样式：</p>

<ul><li><code>slider.bar.size</code> 滑动条宽度，默认值为<code>6</code></li><li><code>slider.thumb.size</code> 滑动块宽高，默认值为<code>8</code></li><li><code>slider.thumb.background</code> 滑动块背景</li></ul>

<p>滑动条图片名称为<code>slider_image</code>，矢量描述如下：</p>

<pre><code>ht.Default.setImage(&#39;slider_image&#39;, {
    width: { func: &#39;getWidth&#39; },
    height: { func: &#39;getHeight&#39; },
    comps: [
        {
            type: &#39;rect&#39;,
            background: { func: &#39;style@live.background.active&#39; },
            rect: {
                func: function (data) {
                    var size = data.s(&#39;slider.bar.size&#39;),
                        radius = data.s(&#39;slider.thumb.size&#39;) + data.s(&#39;live.border.width&#39;),
                        ratio = data._value / (data._max - data._min),
                        isHorizontal = data.isHorizontal(),
                        width = data._width,
                        height = data._height;
                    return {
                        x: isHorizontal ? radius : (width - size) / 2,
                        y: isHorizontal ? (height - size) / 2 : height - radius - (height - radius * 2) * ratio,
                        width: isHorizontal ? (width - radius * 2) * ratio : size,
                        height: isHorizontal ? size : (height - radius * 2) * ratio
                    };
                }
            }
        },
        {
            type: &#39;rect&#39;,
            background: { func: &#39;style@live.background&#39; },
            rect: {
                func: function (data) {
                    var size = data.s(&#39;slider.bar.size&#39;),
                        radius = data.s(&#39;slider.thumb.size&#39;) + data.s(&#39;live.border.width&#39;),
                        ratio = data._value / (data._max - data._min),
                        isHorizontal = data.isHorizontal(),
                        width = data._width,
                        height = data._height;
                    return {
                        x: isHorizontal ? radius + (width - radius * 2) * ratio : (width - size) / 2,
                        y: isHorizontal ? (height - size) / 2 : radius,
                        width: isHorizontal ? (width - radius * 2) * (1 - ratio) : size,
                        height: isHorizontal ? size : (height - radius * 2) * (1 - ratio)
                    };
                }
            }
        },
        {
            type: &#39;circle&#39;,
            borderWidth: { func: &#39;style@live.border.width&#39; },
            borderColor: { func: &#39;style@live.border.color&#39; },
            background: { func: &#39;style@slider.thumb.background&#39; },
            rect: {
                func: function (data) {
                    var radius = data.s(&#39;slider.thumb.size&#39;),
                        ratio = data._value / (data._max - data._min),
                        isHorizontal = data.isHorizontal(),
                        width = data._width,
                        height = data._height;
                    return {
                        x: isHorizontal ? ratio * (width - radius * 2) : width / 2 - radius,
                        y: isHorizontal ? height / 2 - radius : (1 - ratio) * (height - radius * 2),
                        width: radius * 2,
                        height: radius * 2
                    };
                }
            }
        }
    ]
});</code></pre>

<div id="ref_spinner"></div>

<h3>微调按钮</h3>

<p>微调按钮(<code>ht.SpinnerNode</code>)可以设置最大值<code>max</code>、最小值<code>min</code>和当前值<code>value</code>，通过点击上下按钮更改当前值，图片名称为<code>spinner_image</code>，矢量描述如下：</p>

<pre><code>ht.Default.setImage(&#39;spinner_image&#39;, {
    width: { func: &#39;getWidth&#39; },
    height: { func: &#39;getHeight&#39; },
    comps: [
        // Border
        {
            type: { func: &#39;style@live.shape&#39; },
            borderWidth: { func: &#39;style@live.border.width&#39; },
            borderColor: { func: &#39;style@live.border.color&#39; },
            gradient: { func: &#39;style@live.gradient&#39; },
            gradientColor: { func: &#39;style@live.gradient.color&#39; },
            background: {
                func: function (data) {
                    return data.s(data._enabled ? &#39;spinner.background&#39; : &#39;live.background.disabled&#39;);
                }
            },
            rect: [0, 0, 1, 1],
            relative: true
        },
        // Right Top rect
        {
            type: &#39;rect&#39;,
            background: {
                func: function (data) {
                    var style;
                    if (data._topPressed) {
                        style = &#39;live.background.active&#39;;
                    } else if (data._topHover) {
                        style = &#39;live.background.hover&#39;;
                    } else {
                        style = &#39;live.background&#39;;
                    }
                    return data.s(style);
                }
            },
            rect: {
                func: function (data) {
                    var buttonWidth = data.s(&#39;spinner.button.width&#39;),
                        borderWidth = data.s(&#39;live.border.width&#39;);
                    return {
                        x: data._width - buttonWidth,
                        y: borderWidth,
                        width: buttonWidth - borderWidth,
                        height: data._height / 2 - borderWidth
                    };
                }
            }
        },
        // Right Top Arrow
        {
            type: &#39;shape&#39;,
            points: {
                func: function (data) {
                    var buttonWidth = data.s(&#39;spinner.button.width&#39;),
                        width = data._width,
                        height = data._height;
                    return [
                        width - buttonWidth + 0.5 * buttonWidth, 0.15 * height,
                        width - buttonWidth + 0.75 * buttonWidth, 0.4 * height,
                        width - buttonWidth + 0.25 * buttonWidth, 0.4 * height
                    ];
                }
            },
            background: {
                func: function (data) {
                    if (data._topHover) {
                        return &#39;#000000&#39;;
                    }
                    return &#39;#FFFFFF&#39;;
                }
            }
        },
        // Right Bottom rect
        {
            type: &#39;rect&#39;,
            background: {
                func: function (data) {
                    var style;
                    if (data._bottomPressed) {
                        style = &#39;live.background.active&#39;;
                    } else if (data._bottomHover) {
                        style = &#39;live.background.hover&#39;;
                    } else {
                        style = &#39;live.background&#39;;
                    }
                    return data.s(style);
                }
            },
            rect: {
                func: function (data) {
                    var buttonWidth = data.s(&#39;spinner.button.width&#39;),
                        borderWidth = data.s(&#39;live.border.width&#39;),
                        width = data._width,
                        height = data._height;
                    return {
                        x: width - buttonWidth,
                        y: height / 2,
                        width: buttonWidth - borderWidth,
                        height: height / 2 - borderWidth
                    };
                }
            }
        },
        // Right Bottom Arrow
        {
            type: &#39;shape&#39;,
            points: {
                func: function (data) {
                    var buttonWidth = data.s(&#39;spinner.button.width&#39;),
                        width = data._width,
                        height = data._height;
                    return [
                        width - buttonWidth + 0.5 * buttonWidth, 0.9 * height,
                        width - buttonWidth + 0.75 * buttonWidth, 0.65 * height,
                        width - buttonWidth + 0.25 * buttonWidth, 0.65 * height
                    ];
                }
            },
            background: {
                func: function (data) {
                    if (data._bottomHover) {
                        return &#39;#000000&#39;;
                    }
                    return &#39;#FFFFFF&#39;;
                }
            }
        }
    ]
});</code></pre>

<h5><code>SpinnerNode#getValue()</code></h5>

<ul><li>return <code>number</code> 返回当前值</li></ul>

<h5><code>SpinnerNode#setValue(value)</code></h5>

<ul><li><code>value</code> <code>number</code> 当前值</li></ul>

<p>设置微调按钮当前值</p>

<h5><code>SpinnerNode#getMin()</code></h5>

<ul><li>return <code>number</code> 返回最小值，默认值为<code>0</code></li></ul>

<h5><code>SpinnerNode#setMin(value)</code></h5>

<ul><li><code>value</code> <code>number</code> 最小值</li></ul>

<p>设置微调按钮最小值</p>

<h5><code>SpinnerNode#getMax()</code></h5>

<ul><li>return <code>number</code> 返回最大值，默认值为<code>100</code></li></ul>

<h5><code>SpinnerNode#setMax(value)</code></h5>

<ul><li><code>value</code> <code>number</code> 最大值</li></ul>

<p>设置微调按钮最大值</p>

<h5><code>SpinnerNode#getStep()</code></h5>

<ul><li>return <code>number</code> 返回步进，默认值为<code>1</code></li></ul>

<h5><code>SpinnerNode#setStep(value)</code></h5>

<ul><li><code>value</code> <code>number</code> 步进</li></ul>

<p>设置微调按钮步进</p>

<h5><code>SpinnerNode#onChanged</code></h5>

<ul><li><code>function (value)</code> 微调按钮值变化时的回调函数<ul><li><code>value</code> 新的值</li></ul></li></ul>

<p>可以通过如下<code>Style</code>属性设置滑动条的样式：</p>

<ul><li><code>spinner.background</code> 微调按钮背景，默认值为<code>&#39;#FFFFFF&#39;</code></li><li><code>spinner.button.width</code> 微调按钮宽度，默认值为<code>20</code></li></ul>

<div id="ref_combobox"></div>

<h3>组合框</h3>

<p>组合框(<code>ht.ComboboxNode</code>)用于从集合中选择一项，图片名称为<code>combobox_image</code>，矢量描述如下：</p>

<pre><code>ht.Default.setImage(&#39;combobox_image&#39;, {
    width: { func: &#39;getWidth&#39; },
    height: { func: &#39;getHeight&#39; },
    comps: [
        {
            type: { func: &#39;style@live.shape&#39; },
            borderWidth: { func: &#39;style@live.border.width&#39; },
            borderColor: { func: &#39;style@live.border.color&#39; },
            gradient: { func: &#39;style@live.gradient&#39; },
            gradientColor: { func: &#39;style@live.gradient.color&#39; },
            background: { func: &#39;getBackground&#39; },
            rect: [0, 0, 1, 1],
            relative: true
        },
        {
            type: &#39;shape&#39;,
            points: {
                func: function (data) {
                    var buttonWidth = data._buttonWidth,
                        width = data._width,
                        height = data._height;
                    return [
                        width - buttonWidth + 0.5 * buttonWidth, 0.6 * height,
                        width - buttonWidth + 0.75 * buttonWidth, 0.4 * height,
                        width - buttonWidth + 0.25 * buttonWidth, 0.4 * height
                    ];
                }
            },
            background: {
                func: function (data) {
                    if (data._pressed) {
                        return &#39;#000000&#39;;
                    }
                    return &#39;#FFFFFF&#39;;
                }
            }
        },
        {
            type: &#39;text&#39;,
            text: { func: &#39;style@live.label&#39; },
            align: { func: &#39;style@live.label.align&#39; },
            color: { func: &#39;getForeground&#39; },
            font: { func: &#39;style@live.label.font&#39; },
            rect: [0, 0, 1, 1],
            relative: true,
            offsetX: { func: &#39;style@live.label.offset.x&#39; },
            offsetY: { func: &#39;style@live.label.offset.y&#39; }
        }
    ]
});</code></pre>

<h5><code>ComboboxNode#getItems()</code></h5>

<ul><li>return <code>arry</code> 返回可选项集合</li></ul>

<h5><code>ComboboxNode#setItems(value)</code></h5>

<ul><li><code>value</code> <code>arry</code> 可选项集合</li></ul>

<p>设置可选项集合</p>

<h5><code>ComboboxNode#getSelectedItem()</code></h5>

<ul><li>return <code>object</code> 返回当前选中项</li></ul>

<h5><code>ComboboxNode#setSelectedItem(value)</code></h5>

<ul><li><code>value</code> <code>object</code> 当前选中项</li></ul>

<p>设置当前选中项</p>

<h5><code>ComboboxNode#getSelectedIndex()</code></h5>

<ul><li>return <code>number</code> 返回当前选中项索引</li></ul>

<h5><code>ComboboxNode#setSelectedIndex(value)</code></h5>

<ul><li><code>value</code> <code>number</code> 当前选中项索引</li></ul>

<p>设置当前选中项索引</p>

<h5><code>ComboboxNode#getItemName()</code></h5>

<ul><li>return <code>string</code> 返回被选中项的名称，如果被选中项有label属性，则返回被选中项的label属性，否则返回被选中项</li></ul>

<h5><code>ComboboxNode#onChanged</code></h5>

<ul><li><code>function (value)</code> 组合框当前选中项变化时的回调函数<ul><li><code>value</code> <code>object</code> 新的被选中项</li></ul></li></ul>    <hr id="contact" style="margin: 20px 0 1px 0">
    <a href="http://www.hightopo.com"><img class="logo"></a>欢迎交流 <a href="mailto:service@hightopo.com">service@hightopo.com</a>
</body>
</html>
